package com.springboot.frame.web.resubmit;

import com.springboot.frame.web.annotation.ReSubmitLock;
import com.springboot.frame.web.util.WebUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @author liheng
 * @ClassName LockMethodInterceptor
 * @Description 防止重复提交拦截器
 * @date 2019/12/7 16:31
 * @Version 1.0
 */
@Aspect
public class ReSubmitMethodAspect {
    private final static Object PRESENT = new Object();

    private long expire = 0L;


    public ReSubmitMethodAspect(long expire) {
        this.expire = expire;
    }

    @Around("@annotation(com.springboot.frame.web.annotation.ReSubmitLock)")
    public Object interceptor(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        String clazzName = signature.getDeclaringType().getName();
        ReSubmitLock localLock = method.getAnnotation(ReSubmitLock.class);
        String key;
        // 指定一个请求标识
        // 如果扫描到了LocalLock注解，则执行下面程序
        if (localLock.isTokenCheck()) {
            HttpServletRequest request = WebUtils.request();
            String url = request.getRequestURL().toString();
            key = (String) ResubmitLockCache.getLockCache().get(url);
        } else {
            key = getKey(clazzName, method, localLock.key(), pjp.getArgs());
        }

        // 如果全局有有设置，以全局的为准，单位秒
        long delaySeconds = localLock.expire();
        if (this.expire != 0L) {
            delaySeconds = this.expire;
        }
        //执行锁
        boolean lock = false;
        try {
            //设置解锁key
            lock = ResubmitLockCache.getInstance().lock(key, PRESENT);
            if (lock) {
                //放行
                return pjp.proceed();
            } else {
                //响应重复提交异常
                throw new RuntimeException("请勿频繁提交重复请求");
            }

        } catch (Throwable throwable) {
            throw new RuntimeException("服务器异常");
        } finally {
            //设置解锁key和解锁时间
            ResubmitLockCache.getInstance().unLock(lock, key, delaySeconds);
        }
    }

    /**
     * key 的生成策略,如果想灵活可以写成接口与实现类的方式
     *
     * @param keyExpress 表达式
     * @param args       参数
     * @return 生成的key
     */
    private String getKey(String clazzName, Method method, String keyExpress, Object[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append(clazzName);
        sb.append("::" + method.getName() + ":");
        for (Object obj : args) {
            sb.append(obj.toString());
        }
        for (int i = 0; i < args.length; i++) {
            keyExpress = keyExpress.replace("arg[" + i + "]", args[i].toString());
        }
        sb.append(keyExpress);
        return keyExpress;
    }

}
